/**
* This program is distributed "as-is" and is not
* documented beyond the scope of the source-code
* comments.
*
* @author:	JR Lanteigne
* @class:	JET 1 Spring 2014
* @title:	CompPayCalc.cpp
*/

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

/**
* possible employee grades 
* NUM_GRADES is defined for indexing
*/
enum employee_t	{
	EMPLOYEE = 0,
	MANAGER,
	CONSULTANT,
	PRESIDENT,
	NUM_GRADES
};

const int pay_grades[NUM_GRADES] = { 1500, 2500, 4500, 7500 }; // declare pay grades in cents

unsigned int ot_hours[NUM_GRADES] = {0}; // store overtime hours

/**
* calcWeeklyPay(): calculates an employee's pay
* printEmployeePay(): print $ and hrs worked
* Employee_toStr(): returns employee grade as string
*/
double calcWeeklyPay(employee_t employee, unsigned int hours);
double calcWeeklyPay(unsigned int hours);

inline void printEmployeePay(employee_t employee, unsigned int hours);
inline void printEmployeePay(unsigned int hours);

const char *Employee_toStr(employee_t employee);
const char *Employee_toStr();

int main() { // entry point
	unsigned int totalEmployees = 0,
		totalHours = 0,
		_bufferEmployees = 0; // ask how many employees
	unsigned int employeeTypes[NUM_GRADES] = {0}; // count of employees per grade
	double payPerType[NUM_GRADES] = {0};
	double totalPay = 0; // total $$
	char _earlyQuit = 0;

	printf("\n===============================\n|****WEEKLY PAY CALCULATOR****|");
	printf("\n|*ENTER [L] FOR LAST EMPLOYEE*|");
	printf("\n|_____________________________|\n|                             |");
	printf("\n|    [E]mployee\t[M]anager     |"
		   "\n|  [C]onsultant\t[P]resident   |");
	printf("\n===============================");
	printf("\n\nEnter [0] to continue adding employees until [L]ast is reached.");
	printf("\nNumber of employees on payroll: ");
	for(;;) {
		fflush(stdin);
		if(scanf("%u", &_bufferEmployees) == 1) {
			if(_bufferEmployees == 0)
				_bufferEmployees = UINT_MAX;
			break;
		} else
			printf("Invalid value."); // bad user
	}

	/**
	* This is the bread and butter loop.
	* Evaluates data for all company employees.
	*/
	// for(;;) {
	unsigned int i = 0;
	while (i < _bufferEmployees) {
		i++; // increment employee number
		unsigned int hours = 0; // init hours for this employee
		char buff = 'E'; // default is EMPLOYEE
		employee_t employee = EMPLOYEE; // obvious.
		double pay = 0; // init pay for this employee

		printf("\nEntering data for employee #[%u]", i);
			
		for (;;) { // input validation
			static unsigned int n = 0; // loop counter
			char error = 0; // 0 is good, 1 is bad
			fflush(stdin);
			// prompt user for employee type
			printf("\nEmployee type%s: ", (n == 0) ? " [default -> E]" : "");
			n++;

			scanf("%c", &buff); // scan first (char)acter of input buffer
			switch (buff) {
			case 'L':
			case 'l':
				_earlyQuit = 1; // graceful quit
				break;
			case 'E':
			case 'e':
			case '\r':
			case '\n':
				employee = EMPLOYEE;
				break;
			case 'M':
			case 'm':
				employee = MANAGER;
				break;
			case 'C':
			case 'c':
				employee = CONSULTANT;
				break;
			case 'P':
			case 'p':
				employee = PRESIDENT;
				break;
			default:
				error = 1; // assume n00b, set condition for continue
			}
			if (error == 0)
				break; // smart user, move on.
			printf("Invalid value."); // start over...
			n = 0;
		}
		if(!_earlyQuit) {
			printf("%s", Employee_toStr(employee));

			for (;;) { // input validation
				fflush(stdin);
				printf("\nHours worked: ");
				if (scanf("%u", &hours) == 1) {
					if (hours <= 168)
						break; // good, less than 168 hours in a week
				}
				printf("Invalid value, max 168 hours."); // start over...
				}

			switch(employee) {
			case EMPLOYEE:
				pay = calcWeeklyPay(hours);
				break;
			default:
				pay = calcWeeklyPay(employee, hours);
			}

			employeeTypes[employee]++; // increment the count of employees for this type
			totalHours += hours; // add their hours to the total
			totalPay += pay; // add their pay to total payable
			payPerType[employee] += pay;
			totalEmployees++;

			// EMPLOYEE #[1] - MANAGER ($25/hr)
			printf("\n**************\nEMPLOYEE #[%d] - %s ($%.2f/hr)\n", i, Employee_toStr(employee), (double)pay_grades[employee]/100);
			switch(employee) {
			case EMPLOYEE:
				printEmployeePay(hours);
				break;
			default:
				printEmployeePay(employee, hours);
			}
			printf("**************\n");
		} // end loop.
		if(_earlyQuit == 1)
			break;
	}

	// footer section

	/**
	* convert overtime hours to string for printf to read
	* for each type of employee. ot_hours[] is globally
	* defined and is incremented by calcWeeklyPay() every
	* time there is overtime recorded.
	*/
	char employee_ot[17], manager_ot[17], consult_ot[17], pres_ot[17];
	sprintf(employee_ot, "\tOT: [%u hrs]", ot_hours[EMPLOYEE]);
	sprintf(manager_ot, "\tOT: [%u hrs]", ot_hours[MANAGER]);
	sprintf(consult_ot, "\tOT: [%u hrs]", ot_hours[CONSULTANT]);
	sprintf(pres_ot, "\tOT: [%u hrs]", ot_hours[PRESIDENT]);

	/**
	* this section prints the summary as a weekly total
	* of all hours worked and by each employee type
	*/
	printf("\n****** WEEKLY TOTALS ******\n");
	printf("EMPLOYEE%s:\t\t %u ($%.2f)%s\n", (employeeTypes[EMPLOYEE]<= 1) ? "" : "S", employeeTypes[EMPLOYEE], payPerType[EMPLOYEE]/100,
		(ot_hours[EMPLOYEE] == 0 ? "" : employee_ot));
	printf("MANAGER%s:\t\t %u ($%.2f)%s\n", (employeeTypes[MANAGER] <= 1) ? "" : "S", employeeTypes[MANAGER], payPerType[MANAGER]/100,
		(ot_hours[MANAGER] == 0 ? "" : manager_ot));
	printf("CONSULTANT%s:\t\t %u ($%.2f)%s\n", (employeeTypes[CONSULTANT] <= 1) ? "" : "S", employeeTypes[CONSULTANT], payPerType[CONSULTANT]/100,
		(ot_hours[CONSULTANT] == 0 ? "" : consult_ot));
	printf("PRESIDENT%s:\t\t %u ($%.2f)%s\n", (employeeTypes[PRESIDENT] <= 1) ? "" : "S", employeeTypes[PRESIDENT], payPerType[PRESIDENT]/100,
		(ot_hours[PRESIDENT] == 0 ? "" : pres_ot));
	printf("TOTAL EMPLOYED:\t\t %u\n", totalEmployees);
	printf("\nTOTAL BILLED HOURS:\t [%u]\nTOTAL SALARY COST:\t [$%.2f]", totalHours, totalPay/100);
	printf("\n****** END OF REPORT ******\n");
	
	// prompt the user to exit.
	printf("\n[enter] to quit.");
	fflush(stdin);
	getchar();
	return 0; // exit point
}

double calcWeeklyPay(employee_t employee, unsigned int hours) {
	unsigned int	regtime = 40, // default 40 hours
					overtime = 0;
	if (hours > 40) { // store overtime hours
		overtime = hours - 40;
	}
	else { // no overtime
		regtime = hours;
	}

	// push overtime into global ot_hours for each employee type
	ot_hours[employee] += overtime;

	return pay_grades[employee]*(regtime + 1.5 * overtime);
}

double calcWeeklyPay(unsigned int hours) {
	return calcWeeklyPay(EMPLOYEE, hours);
}

inline void printEmployeePay(employee_t employee, unsigned int hours) {
	printf("Pay for employee: %.2f\n", calcWeeklyPay(employee, hours)/100);
	printf("Payable hours: %u\n", hours);
}

inline void printEmployeePay(unsigned int hours) {
	printEmployeePay(EMPLOYEE, hours);
}

const char *Employee_toStr(employee_t employee) {
	switch (employee) {
	case MANAGER:
		return "MANAGER";
		break;
	case CONSULTANT:
		return "CONSULTANT";
		break;
	case PRESIDENT:
		return "PRESIDENT";
		break;
	default:
		return "EMPLOYEE";
	}
}

const char *Employee_toStr() {
	return Employee_toStr(EMPLOYEE);
}